/////////////////////////////////////////////////////
// File Name: switch_top_v1.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年06月28日 星期三 22时41分04秒
/////////////////////////////////////////////////////

module frame_mux(
clk,
rst_n,
mac_rx_ptr_fifo_empty,
mac_rx_ptr_fifo_rd,
mac_rx_ptr_fifo_dout,
mac_rx_data_fifo_rd,
mac_rx_data_fifo_dout,
frame_mux_ptr_fifo_empty,
frame_mux_ptr_fifo_rd,
frame_mux_ptr_fifo_dout,
frame_mux_data_fifo_rd,
frame_mux_data_fifo_dout
);

parameter       PORT_NUM            =   4;
parameter       DATA_FIFO_DEPTH     =   4096;
parameter       DATA_FIFO_WIDTH     =   8;
parameter       DATA_FIFO_PTR_WIDTH =   $clog2(DATA_FIFO_DEPTH);
parameter       PTR_FIFO_DEPTH      =   32;
parameter       PTR_FIFO_WIDTH      =   16;
parameter       FRAME_MAX_LEN_BIT   =   11;

`define         FRAME_LEN_RANGE     10:0
`define         FRAME_LEN_ERR_BIT   14
`define         FRAME_CRC_ERR_BIT   15
`define         FRAME_PORT_RANGE    13:11


input                                           clk;
input                                           rst_n;

input   [PORT_NUM-1:0]                          mac_rx_ptr_fifo_empty;
output  [PORT_NUM-1:0]                          mac_rx_ptr_fifo_rd;
input   [PORT_NUM-1:0] [PTR_FIFO_WIDTH-1:0]     mac_rx_ptr_fifo_dout;
output  [PORT_NUM-1:0]                          mac_rx_data_fifo_rd;
input   [PORT_NUM-1:0] [DATA_FIFO_WIDTH-1:0]    mac_rx_data_fifo_dout;

output                                          frame_mux_ptr_fifo_empty;
input                                           frame_mux_ptr_fifo_rd;
output  [PTR_FIFO_WIDTH-1:0]                    frame_mux_ptr_fifo_dout;
input                                           frame_mux_data_fifo_rd;
output  [DATA_FIFO_WIDTH-1:0]                   frame_mux_data_fifo_dout;


//rrarb
wire    [PORT_NUM-1:0]                          mac_r_req;
wire                                            mac_r_grant_vld;
wire    [PORT_NUM-1:0]                          mac_r_grant;
wire                                            mac_r_switch_to_next; 
//demux
wire    [PTR_FIFO_WIDTH-1:0]                    mux_ptr_dout;
wire    [DATA_FIFO_WIDTH-1:0]                   mux_data_dout;

//frame field
wire                                            err_frame;
wire    [`FRAME_LEN_RANGE]                      frame_len;
wire    [PORT_NUM-1:0]                          frame_port_idx;
wire                                            frame_vld;
//mar fifo operation
reg     [`FRAME_LEN_RANGE]                      mac_data_fifo_pop_cnt; 
wire                                            frame_mux_ptr_fifo_wr;
wire                                            frame_mux_data_fifo_wr;
wire                                            frame_mux_data_fifo_full;
//bp
wire                                            bp;
wire    [DATA_FIFO_PTR_WIDTH:0]                 data_fifo_wr_cnt;
wire                                            ptr_fifo_full;
/*------------------------------------------------------------
rrarb choose one mac
------------------------------------------------------------*/
assign bp = (data_fifo_wr_cnt>DATA_FIFO_DEPTH-1518) | ptr_fifo_full;

assign mac_r_req[PORT_NUM-1:0] = ~(mac_rx_ptr_fifo_empty | {PORT_NUM{bp}});

assign mac_r_switch_to_next = mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE]==mux_ptr_dout[`FRAME_LEN_RANGE]-1'b1;

rrarb_ff #(
        .REQ_CNT(PORT_NUM)
    )x_rrarb_mac_r(
    .clk(clk),
    .rst_n(rst_n),
    .req(mac_r_req[PORT_NUM-1:0]),
    .grant(),
    .grant_ff(mac_r_grant[PORT_NUM-1:0]),
    .grant_vld(mac_r_grant_vld),
    .switch_to_next(mac_r_switch_to_next)
);


one_hot_demux_2d #(
        .WIDTH(1),
        .CNT(PORT_NUM)
    )x_one_hot_demux_mac_state(
    .din(mac_r_switch_to_next),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mac_rx_ptr_fifo_rd[PORT_NUM-1:0])
);

one_hot_mux_2d #(
        .WIDTH(PTR_FIFO_WIDTH),
        .CNT(PORT_NUM)
    )x_one_hot_mux_mac_state(
    .din(mac_rx_ptr_fifo_dout),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mux_ptr_dout[PTR_FIFO_WIDTH-1:0]),
    .err()
);



//rd data fifo
one_hot_demux_2d #(
        .WIDTH(1),
        .CNT(PORT_NUM)
    )x_one_hot_demux_mac_data(
    .din(mac_r_grant_vld),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mac_rx_data_fifo_rd[PORT_NUM-1:0])
);


one_hot_mux_2d #(
        .WIDTH(DATA_FIFO_WIDTH),
        .CNT(PORT_NUM)
    )x_one_hot_mux_mac_data(
    .din(mac_rx_data_fifo_dout),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mux_data_dout[DATA_FIFO_WIDTH-1:0]),
    .err()
);


assign err_frame = mac_r_grant_vld ? (mux_ptr_dout[`FRAME_LEN_ERR_BIT] | mux_ptr_dout[`FRAME_CRC_ERR_BIT]) : 1'b0 ; 

assign frame_vld = mac_r_grant_vld & (~err_frame);

assign frame_len[`FRAME_LEN_RANGE] = mux_ptr_dout[`FRAME_LEN_RANGE] - 3'd4;   //ignore crc 4B

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE] <= {FRAME_MAX_LEN_BIT{1'b0}};
    else if(mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE] == mux_ptr_dout[`FRAME_LEN_RANGE]-1'b1)
        mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE] <= {FRAME_MAX_LEN_BIT{1'b0}};
    else if(mac_r_grant_vld)
        mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE] <= mac_data_fifo_pop_cnt[`FRAME_LEN_RANGE] + 1'b1;
end


assign frame_mux_data_fifo_wr  =  frame_vld && (mac_data_fifo_pop_cnt[10:0]<frame_len[10:0]);

assign frame_mux_ptr_fifo_wr = (mac_data_fifo_pop_cnt[10:0]==frame_len[10:0]) & frame_vld;


assign frame_port_idx[PORT_NUM-1:0] = (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1})    ? 4'b0001 :
                                      (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<1) ? 4'b0010 :  
                                      (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<2) ? 4'b0100 :
                                      (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<3) ? 4'b1000 : 4'b0000;



sfifo #(
    .DEPTH(PTR_FIFO_DEPTH),
    .WIDTH(PTR_FIFO_WIDTH)
)
x_ptr_fifo(
   .clk(clk),
   .data_in({1'b0,frame_port_idx[PORT_NUM-1:0],frame_len[`FRAME_LEN_RANGE]}),
   .data_out(frame_mux_ptr_fifo_dout[PTR_FIFO_WIDTH-1:0]),
   .empty_n(),
   .empty(frame_mux_ptr_fifo_empty),
   .full_n(),
   .full(ptr_fifo_full),
   .rd_en(frame_mux_ptr_fifo_rd),
   .rst_n(rst_n),
   .wr_en(frame_mux_ptr_fifo_wr),
   .almost_full_n(),
   .almost_empty_n(),
   .cnt()
   );


sfifo #(
    .DEPTH(DATA_FIFO_DEPTH),
    .WIDTH(DATA_FIFO_WIDTH)
)
x_data_fifo(
   .clk(clk),
   .data_in(mux_data_dout[DATA_FIFO_WIDTH-1:0]),
   .data_out(frame_mux_data_fifo_dout[DATA_FIFO_WIDTH-1:0]),
   .empty_n(),
   .empty(),
   .full_n(),
   .full(),
   .rd_en(frame_mux_data_fifo_rd),
   .rst_n(rst_n),
   .wr_en(frame_mux_data_fifo_wr),
   .almost_full_n(),
   .almost_empty_n(),
   .cnt(data_fifo_wr_cnt)
   );

endmodule

